package com.cn.pw.frame.security;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.RegexUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;

public class SecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
	
    private static final Logger logger = Logger.getLogger(SecurityMetadataSource.class);
	 
	/** urlMatcher **/
	private UrlMatcher urlMatcher;

	/** useAntPath **/
	private boolean useAntPath = true;

	/** lowercaseComparisons **/
	private boolean lowercaseComparisons = true;

	/** FILENAME **/
	private static final String FILENAME = "privilege.properties";
	
	/** AUTHORITIES_MAP **/
	private static final Map<String, Set<ConfigAttribute>> AUTHORITIES_MAP = new HashMap<String, Set<ConfigAttribute>>();

	/**
	 * @param useAntPath the useAntPath to set
	 */
	public void setUseAntPath(boolean useAntPath) {
		this.useAntPath = useAntPath;
	}

	/**
	 * @param lowercaseComparisons
	 */
	public void setLowercaseComparisons(boolean lowercaseComparisons) {
		this.lowercaseComparisons = lowercaseComparisons;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void init(){

		this.urlMatcher = new RegexUrlPathMatcher();

		if (useAntPath) {
			this.urlMatcher = new AntUrlPathMatcher();
		}

		if (lowercaseComparisons) {
			if (!this.useAntPath) {
				((RegexUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(true);
			}
		} else if (!lowercaseComparisons) {
			if (this.useAntPath) {
				((AntUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(false);
			}
		}
		
		Properties prop = new Properties();
		InputStream in = null;
		Boolean debug = logger.isDebugEnabled();
		try {			
			if (debug) {
				logger.debug("Begin to parse privilege file!");
			}
			in = new BufferedInputStream((new ClassPathResource(FILENAME)).getInputStream());
			prop.load(in);
		} catch (IOException e) {
			logger.error(e.getMessage());
			throw new RuntimeException(e);
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				logger.error(e.getMessage());
			}
		}
		
		Set<String> set = prop.stringPropertyNames();
		
		for (String key : set) {
			Set<ConfigAttribute> grantedAuthorities = new HashSet<ConfigAttribute>();
			String value = (String) prop.get(key);
			String[] array = StringUtils.split(value, ",");
			for (String roleValue : array) {
				if (StringUtils.isNotBlank(roleValue)) {
					grantedAuthorities.add(new SecurityConfig(StringUtils.trim(roleValue)));
				}
			}
			AUTHORITIES_MAP.put(key, grantedAuthorities);
		}
		
		if (debug) {
			logger.debug("Finish parsing privilege file!");
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.intercept.ObjectDefinitionSource#getAttributes
	 * (java.lang.Object)
	 */
	public Collection<ConfigAttribute> getAttributes(Object filter)
			throws IllegalArgumentException {

		FilterInvocation filterInvocation = (FilterInvocation) filter;
		String requestURI = filterInvocation.getRequestUrl();
		Map<String, Set<ConfigAttribute>> urlAuthorities = AUTHORITIES_MAP;

		Set<ConfigAttribute> grantedAuthorities = null;

		for (Map.Entry<String, Set<ConfigAttribute>> entry : urlAuthorities.entrySet()) {
			String url = entry.getKey();
			if (urlMatcher.pathMatchesUrl(url, requestURI)) {
				grantedAuthorities = entry.getValue();
				break;
			}
		}

		if (CollectionUtils.isEmpty(grantedAuthorities)) {
			throw new AccessDeniedException("The url '" + requestURI + "' is illegal.");
		}
		return grantedAuthorities;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.intercept.ObjectDefinitionSource#supports
	 * (java.lang.Class)
	 */

	
	@SuppressWarnings("rawtypes")
	public boolean supports(Class clazz) {
		return true;
	}

	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}
}
